package core.swagger;

import bean.UrlSpit;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import core.db.DbHelper;
import core.db.util.TextUtils;
import core.swagger.model.*;
import core.swagger.utils.JsonUtils;
import entity.ApiAction;
import entity.ApiGroup;
import org.apache.commons.lang3.StringUtils;
import util.CommonUtil;
import util.MD5;
import util.json.JsonMananger;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

public class AnalysisSwagger {
    public static void toAnalysisSwagger(String json,OnCallback callback){
        long time = System.currentTimeMillis();
        AnalysisSwagger swagger = new AnalysisSwagger();
        Map<String,Object> map = null;
        try {
            map = swagger.getTableList(json);
        }catch (Exception e){
            if(callback!=null){
                callback.onError("解析出错:"+e.getMessage());
            }
            return;
        }
        try{
            if(map!=null&&map.get("message")==null){
                String basePath = (String) map.get("basePath");
                String basePathMd5 = MD5.encrypt(basePath);
                Map<String, List<Api>> tableMap = (Map<String, List<Api>>)map.get("tableMap");
                //List<Menu> menus =(List<Menu>)map.get("menu");
                List<ApiAction> apiActions = new ArrayList<>();
                List<ApiGroup> apiGroups = new ArrayList<>();
                int i = 0 ,count = 0 ;
                for(String key:tableMap.keySet()){
                    String pid = MD5.encrypt(key);
                    ApiGroup apiGroup = new ApiGroup();
                    apiGroup.setId(pid);
                    apiGroup.setName(key);
                    apiGroup.setDomain(basePath);
                    apiGroup.setDomainMd5(basePathMd5);
                    apiGroups.add(apiGroup);
                    List<Api> apis = tableMap.get(key);
                    int j = 0;
                    for(Api api:apis){
                        ApiAction apiAction = new ApiAction();
                        String id = MD5.encrypt(api.getUrl());
                        apiAction.setId(id);
                        apiAction.setPid(pid);
                        String url = api.getUrl();
                        UrlSpit urlSpit = CommonUtil.getUrlSpit(url);
                        if(urlSpit!=null){
                            apiAction.setAlias(urlSpit.getAlias());
                            apiAction.setUrl(urlSpit.getUrl());
                            apiAction.setUrlParam(urlSpit.getParam());
                        }
                        apiAction.setDomain(basePath);
                        apiAction.setDomainMd5(basePathMd5);
                        apiAction.setMethod(api.getRequestType());
                        apiAction.setRequestForm(api.getRequestForm());
                        apiAction.setRequest(api.getRequestParam());
                        apiAction.setRespone(api.getResponseParam());
                        apiAction.setRemark(api.getDescription());
                        apiAction.setName(api.getTitle());
                        apiAction.setTag(api.getTag());
                        apiActions.add(apiAction);
                        j++;
                        count++;
                    }
                    i++;
                    if(callback!=null){
                        callback.onProgress(j,apis.size(),"正在解析第["+i+"/"+tableMap.size()+"]组,共"+apis.size()+"条数据,已保存"+count+"条数据..");
                    }
                }
                DbHelper.saveOrUpdate(apiGroups);
                DbHelper.saveOrUpdate(apiActions);
                if(callback!=null){
                    time = System.currentTimeMillis() - time;
                    callback.onSuccess("解析成功!已保存"+count+"条接口数据。耗时:"+time+"ms");
                }
                return;
            }
            if(callback!=null){
                callback.onError("解析出错！"+map.get("message").toString());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return;
    }
    public interface OnCallback{
        void onProgress(int position,int max,String msg);
        void onSuccess(String msg);
        void onError(String msg);
    }
    private Map<String, Object> resultMap = new HashMap<>();
    private List<Menu> menus = new ArrayList<>();
    private  Map<String,Object> getTableList(String swaggerJson){
        List<Api> result = new ArrayList<>();
        Map<String, Object> map = null;
        try {
            map = JsonUtils.readValue(swaggerJson, HashMap.class);
        } catch (Exception e) {
            this.resultMap.put("message", "解析失败！"+e.getMessage());
            return this.resultMap;
        }
        //解析model
        //Map<String, Attr> definitinMap = parseDefinitions(map);
        //解析paths
        Map<String, Map<String, Object>> paths = (Map<String, Map<String, Object>>) map.get("paths");
        if (paths != null) {
            Iterator<Map.Entry<String, Map<String, Object>>> it = paths.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, Map<String, Object>> path = it.next();

                Iterator<Map.Entry<String, Object>> it2 = path.getValue().entrySet().iterator();
                Iterator iterator = path.getValue().keySet().iterator();
                while (it2.hasNext()) {
                    // 1.请求路径
                    String url = path.getKey();

                    // 2.请求方式，类似为 get,post,delete,put 这样
                    String requestType = iterator.next().toString();
                    //String requestType = StringUtils.join(path.getValue().keySet(), ",");

                    // 3. 解析多种请求方式
                    Map.Entry<String, Object> firstRequest = it2.next();
                    Map<String, Object> content = (Map<String, Object>) firstRequest.getValue();

                    // 4. 大标题（类说明）
                    String title = String.valueOf(((List) content.get("tags")).get(0));

                    // 5.小标题 （方法说明）
                    String tag = String.valueOf(content.get("summary"));

                    // 6.接口描述
                    String description = String.valueOf(content.get("description"));


                    // 7.请求参数格式，类似于 multipart/form-data
                    String requestForm = "*/*";
                    List<String> consumes = (List) content.get("consumes");
                    if (consumes != null && consumes.size() > 0) {
                        requestForm = StringUtils.join(consumes, ",");
                    }

                    // 8.返回参数格式，类似于 application/json
                    String responseForm = "*/*";
                    List<String> produces = (List) content.get("produces");
                    if (produces != null && produces.size() > 0) {
                        responseForm = StringUtils.join(produces, ",");
                    }

                    // 9. 请求体
                    List<LinkedHashMap> parameters = (ArrayList) content.get("parameters");

                    // 10.返回体
                    Map<String, Object> responses = (LinkedHashMap) content.get("responses");

                    //封装Table
                    Api table = new Api();



                        Menu menu = new Menu();
                        menu.setUrl(map.get("basePath") + url);
                        menu.setType(3);
                        menu.setTitle(description);
                        menu.setPid(0);
                        menu.setRemark(tag);
                        menu.setMethod(requestType);
                        menu.setIcon("");
                        menus.add(menu);

                        table.setTitle(title);
                        table.setUrl(url);
                        table.setTag(tag);
                        table.setDescription(description);
                        table.setRequestForm(requestForm);
                        table.setResponseForm(responseForm);
                        table.setRequestType(requestType);
                        table.setResponseList(processResponseCodeList(responses));
                    List<Request> requestsList = processRequestList(parameters);
                    try {
                        table.setRequestParam(JsonUtils.writeJsonStr(buildParamMap(requestsList, map)));
                    } catch (Exception e) {
                        this.resultMap.put("message", "json信息解析失败!"+e.getMessage());
                        return this.resultMap;
                    }
                    for (Request request : requestsList) {
                        request.setParamType(request.getParamType().replaceAll("#/definitions/", ""));
                    }
                    table.setRequestList(requestsList);
                    // 取出来状态是200时的返回值
                    Object obj = responses.get("200");
                    if (obj == null) {
                        table.setResponseParam("");
                        result.add(table);
                        continue;
                    }
                    Object schema = ((Map) obj).get("schema");
                    if (schema == null) {
                        table.setResponseParam("");
                        result.add(table);
                        continue;
                    }
                    if (((Map) schema).get("$ref") != null) {
                        //非数组类型返回值
                        String ref = (String) ((Map) schema).get("$ref");
                        //解析swagger2 ref链接
                        ObjectNode objectNode = parseRef(ref, map);
                        table.setResponseParam(objectNode.toString());
                        result.add(table);
                        continue;
                    }
                    Object items = ((Map) schema).get("items");
                    if (items != null && ((Map) items).get("$ref") != null) {
                        //数组类型返回值
                        String ref = (String) ((Map) items).get("$ref");
                        //解析swagger2 ref链接
                        ObjectNode objectNode = parseRef(ref, map);
                        ArrayNode arrayNode = JsonUtils.createArrayNode();
                        arrayNode.add(objectNode);
                        table.setResponseParam(arrayNode.toString());
                        result.add(table);
                        continue;
                    }
                    result.add(table);
                }
            }
        }
        Map<String, List<Api>> tableMap = result.stream().parallel().collect(Collectors.groupingBy(Api::getTitle));
        this.resultMap.put("tableMap", new TreeMap<>(tableMap));
        this.resultMap.put("info", map.get("info"));
        this.resultMap.put("basePath", map.get("basePath"));
        this.resultMap.put("menu",menus);
        return this.resultMap;
    }
    /**
     * 封装post请求体
     *
     * @param list
     * @param map
     * @return
     */
    private Map<String, Object> buildParamMap(List<Request> list, Map<String, Object> map) throws IOException {
        Map<String, Object> paramMap = new HashMap<>(8);
        if (list != null && list.size() > 0) {
            for (Request request : list) {
                String name = request.getName();
                String type = request.getType();
                switch (type) {
                    case "string":
                        paramMap.put(name, "string");
                        break;
                    case "integer":
                        paramMap.put(name, 0);
                        break;
                    case "number":
                        paramMap.put(name, 0.0);
                        break;
                    case "boolean":
                        paramMap.put(name, true);
                        break;
                    case "body":
                        String paramType = request.getParamType();
                        ObjectNode objectNode = parseRef(paramType, map);
                        paramMap = JsonUtils.readValue(objectNode.toString(), Map.class);
                        break;
                    default:
                        paramMap.put(name, null);
                        break;
                }
            }
        }
        return paramMap;
    }

    /**
     * 从map中解析出指定的ref
     *
     * @param ref ref链接 例如："#/definitions/PageInfoBT«Customer»"
     * @param map 是整个swagger json转成map对象
     * @return
     */
    private ObjectNode parseRef(String ref, Map<String, Object> map) {
        ObjectNode objectNode = JsonUtils.createObjectNode();
        if (StringUtils.isNotEmpty(ref) && ref.startsWith("#")) {
            String[] refs = ref.split("/");
            Map<String, Object> tmpMap = map;
            //取出ref最后一个参数 start
            for (String tmp : refs) {
                if (!"#".equals(tmp)) {
                    tmpMap = (Map<String, Object>) tmpMap.get(tmp);
                }
            }
            //取出ref最后一个参数 end
            //取出参数
            if (tmpMap == null) {
                return objectNode;
            }
            Object properties = tmpMap.get("properties");
            if (properties == null) {
                return objectNode;
            }
            Map<String, Object> propertiesMap = (Map<String, Object>) properties;
            Set<String> keys = propertiesMap.keySet();
            //遍历key
            for (String key : keys) {
                Map<String, Object> keyMap = (Map) propertiesMap.get(key);
                if ("array".equals(keyMap.get("type"))) {
                    //数组的处理方式
                    String sonRef = (String) ((Map) keyMap.get("items")).get("$ref");
                    //对象自包含，跳过解析
                    if (ref.equals(sonRef)) {
                        continue;
                    }
                    JsonNode jsonNode = parseRef(sonRef, map);
                    ArrayNode arrayNode = JsonUtils.createArrayNode();
                    arrayNode.add(jsonNode);
                    objectNode.set(key, arrayNode);
                } else if (keyMap.get("$ref") != null) {
                    //对象的处理方式
                    String sonRef = (String) keyMap.get("$ref");
                    //对象自包含，跳过解析
                    if (ref.equals(sonRef)) {
                        continue;
                    }
                    ObjectNode object = parseRef(sonRef, map);
                    objectNode.set(key, object);
                } else {
                    //其他参数的处理方式，string、int
                    String str = "";
                    if (keyMap.get("description") != null) {
                        str = str + keyMap.get("description");
                    }
                    if (keyMap.get("format") != null) {
                        str = str + String.format(",格式为(%s)", keyMap.get("format"));
                    }
                    if (keyMap.get("minLength") != null&&keyMap.get("maxLength") != null) {
                        str = str + String.format(",长度(%s-%s)", keyMap.get("minLength"),keyMap.get("maxLength"));
                    }
                    objectNode.put(key, str);
                }
            }
        }
        return objectNode;
    }

    /**
     * 处理请求参数列表
     *
     * @param parameters
     * @return
     */
    private List<Request> processRequestList(List<LinkedHashMap> parameters) {
        List<Request> requestList = new ArrayList<>();
        if (!TextUtils.isEmpty(parameters)) {
            if (!TextUtils.isEmpty(parameters)) {
                for (Map<String, Object> param : parameters) {
                    Request request = new Request();
                    request.setName(String.valueOf(param.get("name")));
                    Object in = param.get("in");
                    if (in != null && "body".equals(in)) {
                        request.setType(String.valueOf(in));
                        Map<String, Object> schema = (Map) param.get("schema");
                        Object ref = schema.get("$ref");
                        // 数组情况另外处理
                        if (schema.get("type") != null && "array".equals(schema.get("type"))) {
                            ref = ((Map) schema.get("items")).get("$ref");
                        }
                        request.setParamType(ref == null ? "{}" : ref.toString());
                    } else {
                        request.setType(param.get("type") == null ? "Object" : param.get("type").toString());
                        request.setParamType(String.valueOf(in));
                    }
                    if (param.get("required") != null) {
                        request.setRequire((Boolean) param.get("required"));
                    } else {
                        request.setRequire(false);
                    }
                    request.setRemark(String.valueOf(param.get("description")));
                    requestList.add(request);
                }
            }
        }
        return requestList;
    }


    /**
     * 处理返回码列表
     *
     * @param responses 全部状态码返回对象
     * @return
     */
    private List<Response> processResponseCodeList(Map<String, Object> responses) {
        List<Response> responseList = new ArrayList<>();
        Iterator<Map.Entry<String, Object>> resIt = responses.entrySet().iterator();
        while (resIt.hasNext()) {
            Map.Entry<String, Object> entry = resIt.next();
            Response response = new Response();
            // 状态码 200 201 401 403 404 这样
            response.setName(entry.getKey());
            LinkedHashMap<String, Object> statusCodeInfo = (LinkedHashMap) entry.getValue();
            response.setDescription(String.valueOf(statusCodeInfo.get("description")));
            response.setRemark(String.valueOf(statusCodeInfo.get("description")));
            responseList.add(response);
        }
        return responseList;
    }

    /**
     * 解析Definition
     *
     * @param map
     * @return
     */
    private Map<String, Attr> parseDefinitions(Map<String, Object> map) {
        Map<String, Map<String, Object>> definitions = (Map<String, Map<String, Object>>) map.get("definitions");
        Map<String, Attr> definitinMap = new HashMap<>(256);
        if (definitions != null) {
            Iterator<String> modelNameIt = definitions.keySet().iterator();
            while (modelNameIt.hasNext()) {
                String modeName = modelNameIt.next();
                Map<String, Object> modeProperties = (Map<String, Object>) definitions.get(modeName).get("properties");
                if (modeProperties == null) {
                    continue;
                }
                Iterator<Map.Entry<String, Object>> mIt = modeProperties.entrySet().iterator();

                List<Attr> attrList = new ArrayList<>();

                //解析属性
                while (mIt.hasNext()) {
                    Map.Entry<String, Object> mEntry = mIt.next();
                    Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue();
                    Attr modeAttr = new Attr();
                    modeAttr.setName(mEntry.getKey());
                    modeAttr.setType((String) attrInfoMap.get("type"));
                    if (attrInfoMap.get("format") != null) {
                        modeAttr.setType(modeAttr.getType() + "(" + attrInfoMap.get("format") + ")");
                    }
                    modeAttr.setType(StringUtils.defaultIfBlank(modeAttr.getType(), "object"));
                    modeAttr.setDescription((String) attrInfoMap.get("description"));
                    attrList.add(modeAttr);
                }

                Attr modeAttr = new Attr();
                Object title = definitions.get(modeName).get("title");
                Object description = definitions.get(modeName).get("description");
                modeAttr.setClassName(title == null ? "" : title.toString());
                modeAttr.setDescription(description == null ? "" : description.toString());
                modeAttr.setProperties(attrList);
                definitinMap.put("#/definitions/" + modeName, modeAttr);
            }
        }
        return definitinMap;
    }
}
